﻿// ===============================================================================
//  Copyright © ymind.net. All rights reserved .
//  官方网站：http://ymind.net/
//  版权所有：彦铭工作室 老陈出品
// ===============================================================================

using System;
using System.Runtime.InteropServices;
using Lcsoft.TemplateEngine.Tags;

namespace Lcsoft.TemplateEngine.XHtmlParser
{
	/// <summary>
	/// 表示 <see cref="XHtmlDocument"/> 对象的一个节点。
	/// </summary>
	[ComVisible(true)]
	[Serializable]
	public abstract class XHtmlNode
	{
		/// <summary>
		/// 初始化 <see cref="XHtmlNode"/> 类的新实例。
		/// </summary>
		/// <param name="line"><see cref="XHtmlNode"/> 所在的行数。</param>
		/// <param name="column"><see cref="XHtmlNode"/> 所在的列数。</param>
		protected XHtmlNode(int line, int column)
		{
			if (line <= 0) throw new ArgumentOutOfRangeException("line", "参数 line 的值必须大于 0。");
			if (column <= 0) throw new ArgumentOutOfRangeException("column", "参数 column 的值必须大于 0。");

			this.Line = line;
			this.Column = column;
		}

		/// <summary>
		/// 获取父节点。
		/// </summary>
		public XHtmlElement Parent { get; private set; }

		/// <summary>
		/// 获取下一个节点。
		/// </summary>
		public XHtmlNode Next
		{
			get
			{
				var index = this.Index + 1;

				if (index == 0 || this.Parent.Nodes.Count == index) return null;

				return this.Parent.Nodes[index];
			}
		}

		/// <summary>
		/// 获取上一个节点。
		/// </summary>
		public XHtmlNode Previous
		{
			get
			{
				var index = this.Index - 1;

				return index < 0 ? null : this.Parent.Nodes[index];
			}
		}

		/// <summary>
		/// 获取第一个子节点。
		/// </summary>
		public XHtmlNode FirstChild
		{
			get
			{
				var element = this as XHtmlElement;

				if (null != element && element.Nodes.Count > 0) return element.Nodes[0];

				return null;
			}
		}

		/// <summary>
		/// 获取最后一个子节点。
		/// </summary>
		public XHtmlNode LastChild
		{
			get
			{
				var element = this as XHtmlElement;

				if (null != element && element.Nodes.Count > 0) return element.Nodes[element.Nodes.Count - 1];

				return null;
			}
		}

		/// <summary>
		/// 获取 <see cref="XHtmlNode"/> 所在的列数。
		/// </summary>
		public int Column { get; protected set; }

		/// <summary>
		/// 获取 <see cref="XHtmlNode"/> 所在的行数。
		/// </summary>
		public int Line { get; protected set; }

		/// <summary>
		/// 获取 <see cref="XHtmlNode"/> 在集合中的索引。
		/// </summary>
		public int Index
		{
			get { return null == this.Parent ? -1 : this.Parent.Nodes.IndexOf(this); }
		}

		/// <summary>
		/// 获取一个值，该值表示当前 <see cref="XHtmlNode"/> 是否可以转换为 <see cref="XHtmlText"/>。
		/// </summary>
		public bool IsText
		{
			get { return this is XHtmlText; }
		}

		/// <summary>
		/// 获取一个值，该值表示当前 <see cref="XHtmlNode"/> 是否可以转换为 <see cref="XHtmlElement"/>。
		/// </summary>
		public bool IsElement
		{
			get { return this is XHtmlElement; }
		}

		/// <summary>
		/// 获取一个值，该值表示当前 <see cref="XHtmlNode"/> 是否可以转换为 <see cref="XHtmlComment"/>。
		/// </summary>
		public bool IsComment
		{
			get { return this is XHtmlComment; }
		}

		/// <summary>
		/// 获取一个值，该值表示当前 <see cref="XHtmlNode"/> 是否可以转换为 <see cref="XHtmlSSIElement"/>。
		/// </summary>
		public bool IsSSIElement
		{
			get { return this is XHtmlSSIElement; }
		}

		/// <summary>
		/// 获取一个值，该值表示当前 <see cref="XHtmlNode"/> 是否可以转换为 <see cref="TemplateElement"/>。
		/// </summary>
		public bool IsTemplateTag
		{
			get { return this is TemplateElement; }
		}

		/// <summary>
		/// 返回包含 <see cref="XHtmlNode"/> 内容的 .html 格式的字符串。
		/// </summary>
		/// <returns>返回 <see cref="System.String"/>。</returns>
		public abstract string ToHtml();

		/// <summary>
		/// 返回包含 <see cref="XHtmlNode"/> 内容的 .aspx 格式的字符串。
		/// </summary>
		/// <returns>返回 <see cref="System.String"/>。</returns>
		public abstract string ToAspx();

		/// <summary>
		/// 返回表示 <see cref="XHtmlNode"/> 的字符串。
		/// </summary>
		/// <returns>返回 <see cref="System.String"/>。</returns>
		public abstract override string ToString();

		/// <summary>
		/// 判断当前 <see cref="XHtmlNode"/> 是否指定的 <see cref="XHtmlNode"/> 的后代。
		/// </summary>
		/// <param name="node">一个 <see cref="XHtmlNode"/>。</param>
		/// <returns>如果当前 <see cref="XHtmlNode"/> 是 <paramref name="node"/> 的后代则返回 true，否则返回 false。</returns>
		public bool IsDescendentOf(XHtmlNode node)
		{
			var current = this.Parent;

			while (current != null)
			{
				if (current == node) return true;

				current = current.Parent;
			}

			return false;
		}

		/// <summary>
		/// 判断当前 <see cref="XHtmlNode"/> 是否指定的 <see cref="XHtmlNode"/> 的祖先。
		/// </summary>
		/// <param name="node">一个 <see cref="XHtmlNode"/>。</param>
		/// <returns>如果当前 <see cref="XHtmlNode"/> 是 <paramref name="node"/> 的祖先则返回 true，否则返回 false。</returns>
		public bool IsAncestorOf(XHtmlNode node) { return null != node && node.IsDescendentOf(this); }

		/// <summary>
		/// 返回当前 <see cref="XHtmlNode"/> 与指定的 <see cref="XHtmlNode"/> 的一个共同祖先。
		/// </summary>
		/// <param name="node">一个 <see cref="XHtmlNode"/>。</param>
		/// <returns>如果找到当前 <see cref="XHtmlNode"/> 和 <paramref name="node"/> 的共同祖先则返回 true，否则返回 false。</returns>
		public XHtmlNode GetCommonAncestor(XHtmlNode node)
		{
			var current = this;

			while (current != null)
			{
				while (node != null)
				{
					if (current == node) return current;

					node = node.Parent;
				}

				current = current.Parent;
			}

			return null;
		}

		/// <summary>
		/// 移除当前节点。
		/// </summary>
		public void Remove() { if (null != this.Parent) this.Parent.Nodes.RemoveAt(this.Index); }

		/// <summary>
		/// 将指定的 <see cref="XHtmlNode"/> 设置为当前 <see cref="XHtmlNode"/> 的父节点。
		/// </summary>
		/// <param name="parent">一个 <see cref="XHtmlNode"/>。</param>
		internal void SetParent(XHtmlElement parent) { this.Parent = parent; }
	}
}
